package com.sugar.psych.assessment.module.business.service.user;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.sugar.psych.assessment.framework.common.exception.ServiceException;
import com.sugar.psych.assessment.framework.excel.core.util.ExcelUtils;
import com.sugar.psych.assessment.module.business.controller.app.user.vo.AppBizUserRespVO;
import com.sugar.psych.assessment.module.business.controller.app.user.vo.StudentAuthReqVO;
import com.sugar.psych.assessment.module.business.controller.app.user.vo.UserUpdateReqVO;
import com.sugar.psych.assessment.module.business.dal.dataobject.basicconfig.BasicConfigDO;
import com.sugar.psych.assessment.module.business.dal.dataobject.school.SchoolDO;
import com.sugar.psych.assessment.module.business.dal.dataobject.studentauth.StudentAuthDO;
import com.sugar.psych.assessment.module.business.dal.dataobject.userpointsconfig.UserPointsConfigDO;
import com.sugar.psych.assessment.module.business.dal.mysql.avatarinfo.AvatarInfoMapper;
import com.sugar.psych.assessment.module.business.dal.mysql.basicconfig.BasicConfigMapper;
import com.sugar.psych.assessment.module.business.dal.mysql.school.SchoolMapper;
import com.sugar.psych.assessment.module.business.dal.mysql.studentauth.StudentAuthMapper;
import com.sugar.psych.assessment.module.business.dal.mysql.userpointsconfig.UserPointsConfigMapper;
import com.sugar.psych.assessment.module.business.other.tencent.mini.WeChatLoginUtil;
import com.sugar.psych.assessment.module.system.api.auth.AuthApi;
import com.sugar.psych.assessment.module.system.api.auth.dto.AppAuthDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.validation.annotation.Validated;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;

import com.sugar.psych.assessment.module.business.controller.admin.user.vo.*;
import com.sugar.psych.assessment.module.business.dal.dataobject.user.UserDO;
import com.sugar.psych.assessment.framework.common.pojo.PageResult;
import com.sugar.psych.assessment.framework.common.util.object.BeanUtils;

import com.sugar.psych.assessment.module.business.dal.mysql.user.BizUserMapper;
import org.springframework.web.multipart.MultipartFile;

import static com.sugar.psych.assessment.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.sugar.psych.assessment.framework.common.util.collection.CollectionUtils.convertList;
import static com.sugar.psych.assessment.module.business.enums.ErrorCodeConstants.*;

/**
 * 用户信息 Service 实现类
 *
 * @author Ysugar
 */
@Slf4j
@Service
@Validated
public class UserServiceImpl implements UserService {

    @Resource
    private BizUserMapper bizUserMapper;
    @Resource
    private UserPointsConfigMapper userPointsConfigMapper;
    @Resource
    private BasicConfigMapper basicConfigMapper;
    @Resource
    private AuthApi authApi;
    @Resource
    private SchoolMapper schoolMapper;
    @Resource
    private StudentAuthMapper studentAuthMapper;

    private static final ReentrantLock USER_LOCK = new ReentrantLock();
    private static final ReentrantLock STUDENT_AUTH_LOCK = new ReentrantLock();

    @Override
    public Long createUser(UserSaveReqVO createReqVO) {
        // 插入
        UserDO user = BeanUtils.toBean(createReqVO, UserDO.class);
        bizUserMapper.insert(user);
        // 返回
        Long userId = user.getId();

        userPointsConfigMapper.insert(new UserPointsConfigDO().setUserId(userId));
        return userId;
    }

    @Override
    public void updateUser(UserSaveReqVO updateReqVO) {
        // 校验存在
        validateUserExists(updateReqVO.getId());
        // 更新
        UserDO updateObj = BeanUtils.toBean(updateReqVO, UserDO.class);
        bizUserMapper.updateById(updateObj);
    }

    @Override
    public void deleteUser(Long id) {
        // 校验存在
        validateUserExists(id);
        // 删除
        bizUserMapper.deleteById(id);

        userPointsConfigMapper.delete(UserPointsConfigDO::getUserId, id);
    }

    @Override
    public void deleteUserListByIds(List<Long> ids) {
        // 校验存在
        validateUserExists(ids);
        // 删除
        bizUserMapper.deleteByIds(ids);
    }

    private void validateUserExists(List<Long> ids) {
        List<UserDO> list = bizUserMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(USER_NOT_EXISTS);
        }
    }

    private void validateUserExists(Long id) {
        if (bizUserMapper.selectById(id) == null) {
            throw exception(USER_NOT_EXISTS);
        }
    }

    @Override
    public UserRespVO getUser(Long id) {
        return BeanUtils.toBean(bizUserMapper.selectById(id), UserRespVO.class);
    }

    @Override
    public PageResult<UserRespVO> getUserPage(UserPageReqVO reqVO) {
        return bizUserMapper.selectPage(reqVO);
    }

    @Override
    public AppAuthDTO loginByMobileCode(String code) {
        String userMobile = WeChatLoginUtil.getUserMobile(code);

        // 根据 openId 查询用户信息
        UserDO user = bizUserMapper.selectOne(UserDO::getMobile, userMobile);

        // 若为空，则新增用户
        if (Objects.isNull(user)) {
            BasicConfigDO basicConfigDO = basicConfigMapper.selectById(1);

            // 构建用户信息
            user = new UserDO();
            user.setMobile(userMobile);
            if (Objects.nonNull(basicConfigDO))
                user.setAvatar(basicConfigDO.getUserDefaultAvatar());
            USER_LOCK.lock();
            try {
                // 判断如果存在，则直接返回
                UserDO userExists = bizUserMapper.selectOne(UserDO::getMobile, userMobile);
                if (Objects.nonNull(userExists)) {
                    // 更新用户最后一次登录时间
                    bizUserMapper.update(UserDO::getId, userExists.getId(), UserDO::getLastLoginTime, LocalDateTime.now());

                    AppAuthDTO appAuthDTO = authApi.appLogin(userExists.getId(), userExists.getUsername());

                    appAuthDTO.setUserInfo(getBizUser(userExists.getId()));
                    return appAuthDTO;
                }

                String username;
                // 生成唯一账户名，并查询当前生成的账户名是否重复
                while (true) {
                    if (bizUserMapper.selectCount(UserDO::getUsername, username = RandomUtil.randomNumbers(8)) == 0)
                        break;
                }

                user.setUsername(username).setNickname(username).setLastLoginTime(LocalDateTime.now());

                // 插入用户信息
                if (bizUserMapper.insert(user) <= 0)
                    throw exception(USER_CREATE_ERROR);
            } catch (Exception e) {
                log.error("用户注册失败", e);
                throw exception(USER_CREATE_ERROR);
            } finally {
                if (USER_LOCK.isLocked()) {
                    USER_LOCK.unlock();
                }
            }
        } else if (!user.getStatus())
            throw exception(USER_ACCOUNT_FORBIDDEN);
        else
            // 更新用户最后一次登录时间
            bizUserMapper.update(UserDO::getId, user.getId(), UserDO::getLastLoginTime, LocalDateTime.now());

        AppAuthDTO appAuthDTO = authApi.appLogin(user.getId(), user.getUsername());

        appAuthDTO.setUserInfo(getBizUser(user.getId()));
        return appAuthDTO;
    }

    @Override
    public AppBizUserRespVO getBizUser(Long loginUserId) {
        return BeanUtils.toBean(bizUserMapper.selectById(loginUserId), AppBizUserRespVO.class);
    }

    @Override
    public void appUpdateUser(Long loginUserId, UserUpdateReqVO reqVO) {

        String nickname = reqVO.getNickname();
        String avatar = reqVO.getAvatar();
        if (StrUtil.isNotBlank(nickname) || StrUtil.isNotBlank(avatar)) {
            bizUserMapper.update(Wrappers.<UserDO>lambdaUpdate().eq(UserDO::getId, loginUserId)
                    .set(StrUtil.isNotBlank(nickname), UserDO::getNickname, nickname)
                    .set(StrUtil.isNotBlank(avatar), UserDO::getAvatar, avatar));
        }
    }

    @Override
    public void importExcel(MultipartFile file) throws IOException {
        EasyExcel.read(file.getInputStream(), new ReadListener<Map<Integer, String>>() {
            @Override
            public void invoke(Map<Integer, String> data, AnalysisContext context) {
                for (int i = 0; i < 4; i++) {
                    UserDO userDO = bizUserMapper.selectOne(UserDO::getMobile, data.get(0));
                    if (Objects.nonNull(userDO)) {
                        LambdaUpdateWrapper<UserDO> wrapper = Wrappers.<UserDO>lambdaUpdate()
                                .eq(UserDO::getId, userDO.getId())
                                .set(UserDO::getAuthName, data.get(1))
                                .set(UserDO::getAuthId, data.get(2));

                        // 查询学校信息
                        SchoolDO schoolDO = schoolMapper.selectOne(SchoolDO::getName, data.get(3));
                        if (schoolDO != null)
                            wrapper.set(UserDO::getSchoolId, schoolDO.getId());

                        bizUserMapper.update(wrapper);
                    }

                }
            }

            // 读取完毕
            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
            }
        }).sheet().doRead();
    }

    @Override
    public void studentAuth(StudentAuthReqVO reqVO, Long loginUserId) {
        UserDO userDO = bizUserMapper.selectById(loginUserId);
        if (null == userDO)
            throw exception(USER_NOT_EXISTS);
        SchoolDO school = schoolMapper.selectById(reqVO.getSchoolId());
        if (null == school)
            throw exception(SCHOOL_NOT_EXISTS);

        try {
            STUDENT_AUTH_LOCK.lock();

            StudentAuthDO studentAuthDO = studentAuthMapper.selectOne(Wrappers.<StudentAuthDO>lambdaQuery()
                    .eq(StudentAuthDO::getAuthId, reqVO.getId())
                    .eq(StudentAuthDO::getAuthName, reqVO.getName())
                    .eq(StudentAuthDO::getSchoolId, reqVO.getSchoolId())
                    .last("LIMIT 1"));
            if (Objects.isNull(studentAuthDO))
                throw exception(STUDENT_AUTH_NOT_EXISTS);

            if (studentAuthDO.getIsAuth())
                throw exception(STUDENT_AUTH_IS_AUTHED);

            // 更新学生认证信息
            studentAuthMapper.update(Wrappers.<StudentAuthDO>lambdaUpdate()
                    .eq(StudentAuthDO::getId, studentAuthDO.getId())
                    .set(StudentAuthDO::getIsAuth, 1)
                    .set(StudentAuthDO::getMobile, userDO.getMobile()));

            // 更新用户的认证信息
            bizUserMapper.update(Wrappers.<UserDO>lambdaUpdate()
                    .eq(UserDO::getId, loginUserId)
                    .set(UserDO::getAuthName, reqVO.getName())
                    .set(UserDO::getAuthId, reqVO.getId())
                    .set(UserDO::getSchoolId, school.getId()));
        } catch (ServiceException se) {
            throw se;
        } catch (Throwable e) {
            log.error("认证失败：", e);
            throw exception(STUDENT_AUTH_ERROR);
        } finally {
            if (STUDENT_AUTH_LOCK.isLocked()) {
                STUDENT_AUTH_LOCK.unlock();
            }
        }
    }

}